﻿using Cmsp3.Common.Domains;
using Demo.Domains.Entities.Sys;
using Demo.PetaPoco.Application.Dtos.Auth;
using Demo.PetaPoco.Application.Services.Menu;
using Demo.PetaPoco.Application.Services.Role;
using Mapster;
using Microsoft.Extensions.DependencyInjection;
using PmSoft.Core;
using PmSoft.Core.Domain.Auth;
using PmSoft.Web.Abstractions.Authorization;

namespace Demo.PetaPoco.Application.Services.User;

/// <summary>
/// 用户认证服务，提供通过手机号或邮箱进行用户认证的操作，支持密码校验及角色信息映射
/// </summary>
[Inject(ServiceLifetime.Scoped, typeof(IUserAuthenticationService))]
public class UserAuthenticationService : IUserAuthenticationService
{
	private readonly UserRepository _userRepository;
	private readonly UserRoleRepository _userRoleRepository;
	private readonly RoleRepository _roleRepository;
	private readonly RoleMenuRepository roleMenuRepository;
	private readonly MenuRepository menuRepository;
	/// <summary>
	/// 构造函数，注入相关的仓储服务
	/// </summary>
	/// <param name="userRepository">用户仓储</param>
	/// <param name="userRoleRepository">用户角色仓储</param>
	/// <param name="roleRepository">角色仓储</param>
	public UserAuthenticationService(UserRepository userRepository,
		UserRoleRepository userRoleRepository,
		RoleRepository roleRepository,
		RoleMenuRepository roleMenuRepository,
		MenuRepository menuRepository)
	{
		_userRepository = userRepository;
		_userRoleRepository = userRoleRepository;
		_roleRepository = roleRepository;
		this.roleMenuRepository = roleMenuRepository;
		this.menuRepository = menuRepository;
	}

	/// <summary>
	/// 验证用户的密码是否正确，并返回认证的用户信息
	/// </summary>
	/// <param name="user">待认证的用户对象</param>
	/// <param name="password">用户输入的密码</param>
	/// <returns>认证后的用户信息</returns>
	private async Task<IAuthedUser> AuthenticateUserAsync(SysUser? user, string password)
	{
		if (user == null)
			throw new PmSoftException("用户或密码错误");

		// 校验密码是否正确
		if (!UserPasswordHelper.CheckPassword(password, user.Password, UserPasswordFormat.MD5))
			throw new PmSoftException("用户或密码错误");

		// 获取用户的认证信息
		return await GetAuthUserAsync(user);
	}

	/// <summary>
	/// 使用手机号码进行认证登录
	/// </summary>
	/// <param name="mobile">手机号码</param>
	/// <param name="password">密码</param>
	/// <returns>认证后的用户信息</returns>
	public async Task<IAuthedUser> AuthenticateByMobileAsync(string mobile, string password)
	{
		// 获取用户信息
		var user = await _userRepository.GetUserByMobileAsync(mobile);

		// 校验密码并返回认证信息
		return await AuthenticateUserAsync(user, password);
	}

	/// <summary>
	/// 使用邮箱进行认证登录
	/// </summary>
	/// <param name="email">邮箱地址</param>
	/// <param name="password">密码</param>
	/// <returns>认证后的用户信息</returns>
	public async Task<IAuthedUser> AuthenticateByEmailAsync(string email, string password)
	{
		// 获取用户信息
		var user = await _userRepository.GetUserByEmailAsync(email);

		// 校验密码并返回认证信息
		return await AuthenticateUserAsync(user, password);
	}

	/// <summary>
	/// 根据用户ID获取认证后的用户信息
	/// </summary>
	/// <param name="userId">用户ID</param>
	/// <param name="tenantType">租户类型</param>
	/// <returns>认证后的用户信息</returns>
	public async Task<IAuthedUser> GetAuthUserAsync(int userId, string? tenantType)
	{
		// 获取用户信息，若用户不存在则抛出异常
		var user = await _userRepository.GetAsync(userId);
		if (user == null)
			throw new PmSoftException("用户不存在");

		// 返回认证后的用户信息
		var authedUser = await GetAuthUserAsync(user);
		return authedUser;
	}

	/// <summary>
	/// 根据 SysUser 对象获取认证后的用户信息
	/// </summary>
	/// <param name="user">待认证的 SysUser 对象</param>
	/// <returns>认证后的用户信息</returns>
	public async Task<IAuthedUser> GetAuthUserAsync(SysUser user)
	{
		// 将 SysUser 映射为 AuthUser
		var authUser = user.MapTo<SysUser, AuthUser>(setter =>
		{
			setter.Map(d => d.UserId, s => s.Id);
			setter.Map(d => d.UserType, s => s.Type.ToString());
		});

		// 获取用户角色并映射到 AuthUser
		var userRoles = await _userRoleRepository.GetUserRolesAsync(user.Id);
		var roles = await _roleRepository.GetEntitiesByIdsAsync(userRoles.Select(m => m.RoleId));
		authUser.Roles = roles.Adapt<UserRole[]>();
		authUser.TenantType = "SysUser";
		authUser.UserType = "SuperAdministrator";
		// 返回认证后的用户信息
		return authUser;
	}

	public async Task<IEnumerable<IMenu>> GeUserMenusAsync(IAuthedUser user)
	{
		var roleMenus = new List<SysRoleMenu>();
		foreach (var role in user.Roles)
		{
			roleMenus.AddRange(await roleMenuRepository.GetRoleMenusAsync(role.Id));
		}
		IEnumerable<IMenu> menus = await menuRepository.GetEntitiesByIdsAsync(roleMenus.Select(m => m.MenuId));
		return menus;
	}
}
